package common

import (
	"errors"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"time"

	"reflect"

	"gitee.com/dayu1985/framework/utils"
)

//MongoServiceContract Mongodb service contract
type MongoServiceContract interface {

	//Indexes to build
	Indexes() error

	//Item value
	VItem() interface{}

	//Items value
	VItems() interface{}
}

//MongoService MongoDB相关的服务
type MongoService struct {
	MongoServiceContract
	Collection *mgo.Collection
}

//POST RESTful - create object
func (s MongoService) POST(bean interface{}) error {

	vbean := reflect.ValueOf(bean).Elem()
	vbean.FieldByName("ID").Set(reflect.ValueOf(utils.PtrOfString(bson.NewObjectId().Hex())))
	vbean.FieldByName("Status").Set(reflect.ValueOf(utils.PtrOfInt(1)))
	vbean.FieldByName("Created").Set(reflect.ValueOf(utils.PtrOfInt64(time.Now().Unix())))

	return s.Collection.Insert(bean)
}

//PUT RESTful - edit object
func (s MongoService) PUT(id interface{}, bean interface{}, result interface{}) error {

	vbean := reflect.ValueOf(bean).Elem()
	vbean.FieldByName("Changed").Set(reflect.ValueOf(utils.PtrOfInt64(time.Now().Unix())))

	err := s.Collection.UpdateId(id, bson.M{"$set": bean})

	if err != nil {
		return err
	}

	return s.Collection.FindId(id).One(result)
}

//DELETE RESTful - delete object
func (s MongoService) DELETE(id interface{}) error {

	return s.Collection.RemoveId(id)
}

//GET RESTful - get object
func (s MongoService) GET(id interface{}, result interface{}) error {

	return s.Collection.FindId(id).One(result)
}

//QUERY - query object list
func (s MongoService) QUERY(query bson.M) *mgo.Query {

	return s.Collection.Find(query)
}

//UPDATES - update all object
func (s MongoService) UPDATES(m *Updates) (*mgo.ChangeInfo, error) {

	if len(m.Update) == 0 {
		return nil, errors.New("No update cond.")
	}

	return s.Collection.UpdateAll(m.Query, m.Update)
}

//UPSERT - update all object
func (s MongoService) UPSERT(Selector bson.M, Data interface{}) (*mgo.ChangeInfo, error) {
	return s.Collection.Upsert(Selector, Data)
}

func (s MongoService) QUERYONE(query bson.M, result interface{}) error {

	return s.Collection.Find(query).One(result)
}

func (s *MongoService) PAGE(m *PageQuery, items interface{}) (result *PageResult, err error) {

	count := -1
	q := s.QUERY(m.Query)

	if len(m.Fields) > 0 {
		q.Select(m.Fields)
	}

	if !m.Waterfall {

		var err error
		count, err = q.Count()

		if err != nil {
			return nil, err
		}
	}

	page := m.Page

	if m.PageSize == 0 {
		m.PageSize = 16
	}

	if page > 0 {
		page -= 1
	}

	q.Skip(page * m.PageSize)

	if len(m.Sort) > 0 {
		q.Sort(m.Sort...)
	} else {
		q.Sort("_id")
	}

	if m.PageSize > 0 {
		q.Limit(m.PageSize)
	}

	err = q.All(items)

	if err != nil {
		return nil, err
	}

	out := PageResult{}

	out.Page = m.Page
	out.Size = m.PageSize
	out.Total = count

	return &out, nil
}
